#ifndef _DLT645_H_
#define _DLT645_H_
#include "protocol.h"
#include "libutils.h"
#include "meter.h"
#include "concentrator.h"

enum FUNCODE{
	RESERVED=0,	//保留
	READ=1,		//读数据
	READAGAIN=2,//读后续数据
	REREAD=3,	//重读数据
	WRITE=4,		//写数据
	SETTIME=8,	//广播校时
	WRITE_DEV_ADDR=0xA,	//写设备地址
	CHANGE_PWD=0xF,		//更改通信速率
	ZERO=0x10,			//最大需量清零
};

enum DIRECTION{
	ZHU_CONG = 0,
	CONG_ZHU = 1,
};

#pragma pack(1)
typedef struct dlt645_control {
	__u8 funcode:5;
	__u8 subframes:1;
	__u8 responseID:1;
	__u8 direction:1;
} dlt645_control;

// data tag: DI1(DI1H, DI1L) & DI0(DI0H, DI0L)
typedef struct data_tag {
	__u8 DI0L:4;
	__u8 DI0H:4;
	__u8 DI1L:4;
	__u8 DI1H:4;
} data_tag;

typedef struct dlt645 {
	__u8 start1;
	__u8 addr[6];
	__u8 start2;
	union _control{
		dlt645_control dc;
		__u8 C1;
	}control;
	__u8 len;
	union _data{
		struct _with_di{
			__u8 di0;
			__u8 di1;
			__u8 data[254];
		}with_di;
		struct _raw{
			__u8 data[256];
		}raw;
	}_data1;
	__u8 cs;
	__u8 end;
} dlt645;
#pragma pack()

extern meter m;

/* 检验是否是合法的dlt645数据 
** 检验两个帧起始符，结束符，以及校验和
*/
int dlt645_valid_check(void *pdata, int len);
int add33(__u8 *data, __u8 len);
int sub33(__u8 *data, __u8 len);

/*
电表模拟实现：
1. 读数据
	可对"主站请求帧"响应，返回"从站正常应答"或"异常应答"
	请 求 帧：C=01H L=02H DI0DI1
	正常应答：C=81H/A1H(无/有后续帧) L=02H+M DI0DI1 DATA 
	异常应答：C=C1H L=01H ERR_CODE
 2. 读后续数据
	可对"主站请求后续帧"响应，返回“从站正常应答”或“异常应答”
	请 求 帧：C=02H L=02H
	正常应答：C=82H/A2H（无/有后续帧） L=02H+M DI0DI1 DATA
	异常应答：C=C2H L=01H ERR_CODE
3. 重读数据	TODO
4. 写数据
	可对“主站设置数据请求”响应，返回从站“正常”或“异常”应答
	请 求 帧：C=04H L=02H DI0DI1
	正常应答：C=84H L=00H
	异常应答：C=C4H L=01H err_code
5. 广播校时
	可对“主站广播校时请求”响应，不要求应答
	请 求 帧：C=08H L=06H DATA=YYMMDDHHMMSS
*/

//封装645结构体，为645结构体各个字段赋值
void make_dlt645(dlt645 *pdata, __u8 *addr, __u8 funcode,
	__u8 subframe, __u8 responseID, __u8 dir,
	__u8 di0, __u8 di1, __u8 *buf, __u8 len);

//把645结构体拷贝到raw指针指向的内存，准备发送该内存
__u8* dlt6452raw(dlt645 *pdata, __u8 *raw, __u8 *len);

int dlt645_response(dlt645 *p_request, int len, meter *m, concentrator *c);
#endif
